<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        // demo2.1
        // 内置对象
        // 非构造器对象（Math、JSON） 不用new一个对象直接使用即可
        // 构造器对象（Object、Array、Function、Number、String、Boolean、Date等）
        var num = new Number();
        console.log(dir(num))
        var arr = new Array(3);
        Array instanceof Function; //t
        Array instanceof Object; //t
        Math instanceof Object; //t
        Math instanceof Function; //false

        //Part 1
        var i = new String("str"); // String Object
        var h = new Number(1); // Number Object
        var g = new Boolean(true); // Boolean Object
        var j = new Object({name: "Tom"}); // Object Object
        var k = new Array([1, 2, 3, 4]); // Array Object
        var l = new Date(); // Date Object
        var m = new Error();
        var n = new Function();
        var o = new RegExp("\\d");

        // typeof 构造函数！！ arr -> Object
        console.log(typeof Array); //function
        console.log(typeof Function); //function
        console.log(typeof Date); //function
        console.log(typeof Number); //function
        console.log(typeof String); //function
        console.log(typeof Boolean); //function
        console.log(typeof Math); //和上边一样么？为什么 object
        console.log(typeof JSON); //object

        Object instanceof Function;// true
        Object instanceof Object;// true
        Number instanceof Function;// true
        Number instanceof Object;// true
        Math instanceof Function;// false
        Math instanceof Object;// true

        // demo2.2
        var obj = {
            PI: 3.14
        };
        obj.toString(); //可以成功访问？obj的父元素有tostring()这个方法，可以调通
        // '[object Object]' 为什么是这个 因为这个太复杂了所以直接简化成了这样
        // 可以用json.stringify 转换成json字符串
        
        //修改Math对象的PI属性  修改成功？
        console.log(Math.PI);
        Math.PI = 3;
        console.log(Math.PI);  //3.141592653589793
        // 不成功
        //修改自定义对象obj对象的PI属性  修改成功？
        var obj = {
            PI: 3.14
        };
        console.log(Math.PI); //3.141592653589793
        obj.PI = 3;
        console.log(obj.PI); //3
        // Math.PI不变 obj.PI改变

        //遍历对象属性demo2.2-1
        var obj = {
            PI:3.14
        };
        obj.__proto__ = {x: "xx"}
        for(var key in obj){
            console.log(key)
        }
        // 不仅输出本身还输出上级的属性 PI x

        //遍历对象属性demo2.2-2
        var obj = {
            PI: 3.14
        };
        for (var key in obj) {
            if (key == "toString") {      //遍历tostring属性
                //能否遍历出？ 不能，因为上级元素的enumerable : false(即不可枚举)
                console.log(true);
                break;
            }
        }

        // demo2.3
        // Object.defineProperty(obj, prop, descriptor)
        // obj —— 要在其上定义属性的对象;prop —— 要定义或修改的属性的名称;descriptor —— 将被定义或修改的属性描述符
        // value 默认为 undefined; 
        // enumerable、writable、configurable 默认为 false

        // 设置单个属性描述符
        // 隐式定义
        var obj = {};
        var descriptor = {};
        // 默认没有 enumerable，没有 configurable，没有 writable false
        descriptor.value = "static";
        Object.defineProperty(obj, "key1", descriptor);

        // 显式定义
        Object.defineProperty(obj, "key2", {
            enumerable: false,
            configurable: false,
            writable: false,
            value: "static"
        });

        obj.key2 = "123";
        console.log(obj.key2); //static 不可改写性
        for (var i in obj) {
            console.log(i);  //遍历结果没有均不可枚举
        }
        // value（属性的值）：对应属性的值
        // writable（可写特性）	：确定属性是否可改写性
        // configurable（可配置特性）：确定属性是否能删除和其他特性是否可配置
        // enumerable（可枚举特性）：属性是否可枚举

        // 一旦把属性的 configurable 设置为 false，就不能再把它变回可配置了。此时，再调用 Object.defineProperty()方法修改除 writable 之外的特性，都会导致错误。
        // 当 configurable 为 false，且 writable 为 false 时，调用 Object.defineProperty()方法修改 writable 为 true 时会报错。
        // 当 configurable 为 false，且 writable 为 true 时，调用 Object.defineProperty() 方法修改 writable 为 false 时会修改成功，不会报错。

        // demo2.5
        // 多个属性值添加
        var obj = {};
        Object.defineProperties(obj, {
            'property1': {
                value: true,
                writable: true
            },
            'property2': {
                value: 'Hello',
                writable: false
            }
            // etc. etc.
        });

        // getOwnPropertyDescriptors
        var obj = {
            PI: 3.14
        };
        Object.defineProperty(obj, "key", {
            value: "123",
            writable: false,
            enumerable: false,
            configurable: true
        });
        Object.getOwnPropertyDescriptors(obj);
        // PI:configurable: true；enumerable: true；value: 3.14；writable: true；
        // 字面量定义默认属性特性为 true
        // key: ·····

        // Object.getOwnPropertyDescriptors(obj) 
        // 获取一个对象的所有自身属性的描述符

        Object.getOwnPropertyDescriptor(Math,"PI");
        Object.getOwnPropertyDescriptors(Object.prototype);

        // demo2.6
        var obj = {
            name: "Lily",
            age: 20,
            hobby: "swimming",
        };
        Object.defineProperty(obj, "married", {
            value: false,
            writable: true,
            enumerable: false,
            configurable: false
        });
        for (var key in obj) {
            console.log(key);
        } //无married(enumerable: false)

        //思考下述代码能否输出x属性，若不想输出x属性，如何通过修改x属性的特性来实现？
        var objOrigin = {x:1}; //可以
        obj.__proto__ = objOrigin;
        for (var key in obj) {
            console.log(key);
        }
        // 能输出x(obj.__proto__ )是父元素的属性可以遍历出
        // 若不想则更改x属性特性enumerable为false
        Object.defineProperty(objOrigin,"x",{
            enumerable:false
        });
        for (var key in obj) {
            console.log(key);
        }

        // demo2.7
        var obj = {
            name: "Lily",
            age: 20
        };
        Object.defineProperty(obj, "married", {
            value: false,
            writable: true,
            enumerable: false,
            configurable: false
        });

        obj.propertyIsEnumerable("name"); //true
        obj.propertyIsEnumerable("married"); //false

        // demo2.8
        var obj2 = {
            PI: 3.14
        };
        Object.defineProperty(obj2, "PI", {
            configurable: false //属性无法删除 其他特性不可配置
        });
        delete obj2.PI;
        console.log(obj2.PI); //3.14

        Object.getOwnPropertyDescriptors(obj2);
        Object.defineProperty(obj2,"PI",{enumerable: false}); //报错
    </script>
</body>
</html>